package top.huxy.web.config.argumentResolver;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import top.huxy.web.core.FromRequestCookie;
import top.huxy.web.core.FromRequestHeader;
import top.huxy.web.core.FromRequestParam;
import top.huxy.web.core.Verifiable;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author aproz
 * @Date 2022/5/11
 */
@Slf4j
public class UniversalArgumentResolver implements HandlerMethodArgumentResolver {

    public UniversalArgumentResolver() {
        log.info("{}已被加载",this.getClass());
    }

    /**
     * Header的缓存key名
     */
    public static final String HEADER_MAP_CACHE_NAME = "HEADER_MAP_CACHE";

    /**
     * Cookie的缓存key名
     */
    public static final String COOKIE_MAP_CACHE_NAME = "COOKIE_MAP_CACHE";

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Serializable.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        //反射生成对象
        Class<?> target = parameter.getParameterType();
        Object o = target.newInstance();
        Field[] fields = target.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value;
            try {
                value = findFieldValue(field, webRequest);
            } catch (Exception e) {
                log.info("寻找字段{}的值时出错", field);
                log.error("寻找字段的值时出错", e);
                throw e;
            }
            try {
                field.set(o, value);
            } catch (Exception e) {
                log.info("赋值{}时出错", field);
                log.error("赋值时出错", e);
                throw e;
            }

        }

        //如果对象实现了Verifiable，则调用validate方法
        if (o instanceof Verifiable) {
            ((Verifiable) o).validate();
        }

        return o;
    }

    /**
     * 从webRequest中生成field所需的值
     *
     * @param field      字段
     * @param webRequest 请求对象
     * @return field对应的值
     */
    private Object findFieldValue(Field field, NativeWebRequest webRequest) {
        //所需要的类型
        Type type = field.getGenericType();

        //含有FromRequestHeader,就从请求头中获取值
        FromRequestHeader fromRequestHeader = field.getAnnotation(FromRequestHeader.class);
        if (fromRequestHeader != null) {
            String value = fromRequestHeader.headerName();
            Object res;
            if ("".equals(value)) {
                res = createValue(type, getHeaderMap(webRequest).get(field.getName().toLowerCase()));
            } else {
                res = createValue(type, getHeaderMap(webRequest).get(value.toLowerCase()));
            }
            if (res == null && fromRequestHeader.defaultValue().length > 0) {
                res = createValue(type, fromRequestHeader.defaultValue());
            }
            return res;
        }

        //含有FromRequestCookie,就从cookie中获取值
        FromRequestCookie fromRequestCookie = field.getAnnotation(FromRequestCookie.class);
        if (fromRequestCookie != null) {
            String value = fromRequestCookie.cookieName();
            Object res;
            if ("".equals(value)) {
                res = createValue(type, getCookieMap(webRequest).get(field.getName().toLowerCase()));
            } else {
                res = createValue(type, getCookieMap(webRequest).get(value.toLowerCase()));
            }
            if (res == null && fromRequestCookie.defaultValue().length > 0) {
                res = createValue(type, fromRequestCookie.defaultValue());
            }
            return res;
        }

        //含有FromRequestParam,就从请求参数中获取值
        FromRequestParam fromRequestParam = field.getAnnotation(FromRequestParam.class);
        if (fromRequestParam != null) {
            String value = fromRequestParam.paramName();
            Object res = null;
            if (!"".equals(value)) {
                res = createValue(type, getParameterMap(webRequest).get(value));
            }
            if (res == null && fromRequestParam.defaultValue().length > 0) {
                res = createValue(type, fromRequestParam.defaultValue());
            }
            return res;
        }

        //没有指定值的来源，默认从请求参数中获取值
        return createValue(type, getParameterMap(webRequest).get(field.getName()));
    }

    private Map<String, String[]> getCookieMap(NativeWebRequest webRequest) {
        Object cache;
        if ((cache = webRequest.getAttribute(COOKIE_MAP_CACHE_NAME, NativeWebRequest.SCOPE_REQUEST)) != null) {
            return (Map<String, String[]>) cache;
        }
        if (webRequest instanceof HttpServletRequest) {
            Cookie[] cookies = ((HttpServletRequest) webRequest).getCookies();
            if (cookies == null || cookies.length == 0) {
                return Collections.emptyMap();
            }
            HashMap<String, String[]> map = new HashMap<>(8);
            for (Cookie cookie : cookies) {
                map.put(cookie.getName(), new String[]{cookie.getValue()});
            }
            webRequest.setAttribute(COOKIE_MAP_CACHE_NAME, map, NativeWebRequest.SCOPE_REQUEST);
            return map;
        }
        log.warn("无法获取Cookie");
        return Collections.emptyMap();
    }

    private Map<String, String[]> getParameterMap(NativeWebRequest request) {
        //大部分请求都对请求参数做了缓存优化，这里就不手动加缓存了
        return request.getParameterMap();
    }

    private Map<String, String[]> getHeaderMap(NativeWebRequest request) {
        Object cache;
        if ((cache = request.getAttribute(HEADER_MAP_CACHE_NAME, NativeWebRequest.SCOPE_REQUEST)) != null) {
            return (Map<String, String[]>) cache;
        }
        Iterator<String> headerNames = request.getHeaderNames();
        HashMap<String, String[]> map = new HashMap<>(16);
        while (headerNames.hasNext()) {
            String name = headerNames.next();
            map.put(name, request.getHeaderValues(name));
        }
        request.setAttribute(HEADER_MAP_CACHE_NAME, map, NativeWebRequest.SCOPE_REQUEST);
        return map;
    }

    /**
     * 将arr转成type类型
     *
     * @param type 想转成的类型
     * @param arr  元数据
     * @return type类型的对象
     */
    private Object createValue(Type type, String[] arr) {
        if (arr == null || arr.length == 0) {
            return null;
        }

        //type是基本数据类型、它们的包装类、String
        if (type instanceof Class) {
            Class<?> t = (Class<?>) type;
            //简单数据类型就用arr[0]解析
            if (ClassUtil.isSimpleValueType(t)) {
                return Convert.convert(type, arr[0]);
            }

        }

        Object o;

        //如果arr长度为1，type又是集合或数组，则只解析arr[0]
        if (arr.length == 1) {
            if (type instanceof Class && Object[].class.isAssignableFrom((Class<?>) type)) {
                o = JSON.parseArray(arr[0]);
            } else if (type instanceof ParameterizedType && Collection.class.isAssignableFrom(
                    (Class<?>) ((ParameterizedType) type).getRawType())
            ) {
                o = JSON.parseArray(arr[0]);
            } else {
                o = JSON.parse(arr[0]);
            }
        } else {
            //如果arr长度不为1，且type是数组或List，则将元数据转成List给o
            if (type instanceof Class && Object[].class.isAssignableFrom((Class<?>) type)) {
                LinkedList<Object> list = new LinkedList<>();
                for (String s : arr) {
                    list.add(JSON.parse(s));
                }
                o = list;
            } else if (type instanceof ParameterizedType && List.class.isAssignableFrom(
                    (Class<?>) ((ParameterizedType) type).getRawType())
            ) {
                LinkedList<Object> list = new LinkedList<>();
                for (String s : arr) {
                    list.add(JSON.parse(s));
                }
                o = list;
            } else {
                //如果type是其他类型的数据，直接转成JSON，但通常执行到这里应该会报错。
                o = JSON.toJSON(arr);
            }
        }
        //用hutool工具类转换
        return Convert.convert(type, o);
    }
}
